/**
 * 模块配置结构
 *
 * 命名习惯是ngx_http_<module name>_(main|srv|loc)_conf_t
 * main：配置文件中最外层，例如daemon
 * srv：配置文件中server层
 * loc：配置文件中location层
 */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct
{
	ngx_str_t hello_string;
	ngx_int_t hello_counter;
}ngx_http_hello_loc_conf_t;

static char* ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char* ngx_http_hello_counters(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void* ngx_http_hello_create_loc_conf(ngx_conf_t *cf);

/**
 * 模块配置指令
 */
static ngx_command_t ngx_http_hello_commands[] = {
	/*对应配置结构体ngx_http_hello_loc_conf_t中的元素hello_string*/
	{
		/*指令名称*/
		ngx_string("hello_string"),
		/**
		 * 指令属性
		 * NGX_HTTP_LOC_CONF:表示属性可以出现在http location层
		 * NGX_CONF_NOARGS:可以不设置
		 * NGX_CONF_TAKE1:接受一个参数
		 * NGX_CONF_FLAG:可以接受的值是"on"或者"off"，最终会被转成bool值
		 * ....
		 */
		NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
		/**
		 * 当nginx在解析配置的时候，如果遇到这个配置指令，
		 * 将会把读取到的值传递给这个函数进行分解处理。
		 * 
		 * 函数原型:char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
		 * cf:保存读取到的配置信息的原始字符串以及相关的一些信息
		 * 这个参数的args字段是一个ngx_str_t类型的数组，每个数组元素。
		 * 该数组的首个元素是这个配置指令本身的字符串，第二个元素是首个参数，
		 * 第三个元素是第二个参数，依次类推。
		 * cmd: 这个配置指令对应的ngx_command_t结构
		 * conf: 就是定义的存储这个配置值的结构体，比如在上面展示的那个ngx_http_hello_loc_conf_t。
		 * 当解析这个hello_string变量的时候，传入的conf就指向一个ngx_http_hello_loc_conf_t类型的变量。
		 * 用户在处理的时候可以使用类型转换，转换成自己知道的类型，再进行字段的赋值。
		 */
		ngx_http_hello_string,
		/**
		 * 该字段指定当前配置项存储的内存位置
		 * NGX_HTTP_MAIN_CONF_OFFSET：配置文件中最外层
		 * NGX_HTTP_SRV_CONF_OFFSET：server层
		 * NGX_HTTP_LOC_CONF_OFFSET：location层
		 */
		NGX_HTTP_LOC_CONF_OFFSET,
		/*指定该配置项值的精确存放位置，即配置结构体中的元素名称*/
		offsetof(ngx_http_hello_loc_conf_t, hello_string),
		/*该字段存储一个指针。可以指向任何一个在读取配置过程中需要的数据，以便于进行配置读取的处理。*/
		NULL
	},

	/*对应配置结构体ngx_http_hello_loc_conf_t中的元素hello_counter*/
	{
		ngx_string("hello_counter"),
		NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
		ngx_http_hello_counters,
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_hello_loc_conf_t, hello_counter),
		NULL
	},

	/*如果有更多...格式与上面一样*/

	/**
	 * 固定结尾
	 * #define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }
	 */
	ngx_null_command
};

/*
typedef struct {
	//在创建和读取该模块的配置信息之前被调用
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
	//在创建和读取该模块的配置信息之后被调用
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
	
    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
	
	//调用该函数创建本模块位于location block的配置信息存储结构。
	//每个在配置中指明的location创建一个。该函数成功的时候，返回创建的配置对象。失败的话，返回NULL。
    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
*/

/*模块上下文结构*/
static ngx_http_module_t ngx_http_hello_module_ctx = {
	NULL,
	NULL,

	NULL,
	NULL,

	NULL,
	NULL,

	ngx_http_hello_create_loc_conf,
	NULL
};

/*模块结构*/
ngx_module_t ngx_http_hello_module = {
	NGX_MODULE_V1,				/*#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1*/
	&ngx_http_hello_module_ctx, /*指定模块上下文结构变量*/
	ngx_http_hello_commands, 	/*指定模块配置指令变量*/
	NGX_HTTP_MODULE, 			/*指定模块类型*/
	NULL, 						/*init master*/
	NULL,						/*init module*/
	NULL,						/*init process*/
	NULL,						/*init pthread*/
	NULL,						/*exit pthread*/
	NULL,						/*exit process*/
	NULL,						/*exit master*/
	NGX_MODULE_V1_PADDING		/*系统预留值 #define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0*/
};

static ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r){
	printf("called:ngx_http_echo_handler\n");
	ngx_int_t 		rc;
	ngx_buf_t 		*b;
	ngx_chain_t 	out;

	ngx_http_hello_loc_conf_t *clcf;
	clcf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);

	if(!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))){
		return NGX_HTTP_NOT_ALLOWED;
	}

	r->headers_out.content_type.len = sizeof("text/html");
	r->headers_out.content_type.data= (u_char*)"text/html";
	r->headers_out.status			= NGX_HTTP_OK;
	r->headers_out.content_length_n = clcf->hello_string.len;

	if(r->method == NGX_HTTP_HEAD){
		rc = ngx_http_send_header(r);

		if(rc == NGX_ERROR || rc > NGX_OK || r->header_only){
			return rc;
		}
	}

	b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
	if(b == NULL){
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate memory.");
		return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	out.buf = b;
	out.next= NULL;

	b->pos = clcf->hello_string.data;
	b->last= clcf->hello_string.data + clcf->hello_string.len;
	b->memory = 1;
	b->last_buf = 1;
	rc = ngx_http_send_header(r);

	if(rc == NGX_ERROR || rc > NGX_OK || r->header_only){
		return rc;
	}

	return ngx_http_output_filter(r, &out);
}

static void* ngx_http_hello_create_loc_conf(ngx_conf_t *cf){
	printf("called:ngx_http_hello_create_loc_conf\n");
	ngx_http_hello_loc_conf_t *conf;

	conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));
	if(conf == NULL){
		return NGX_CONF_ERROR;
	}

	conf->hello_string.len = 0;
	conf->hello_string.data= NULL;
	conf->hello_counter = 0;
	return conf;
}

static char* ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
	printf("called:ngx_http_hello_string\n");
	ngx_http_core_loc_conf_t *clcf;
	clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	clcf->handler = ngx_http_echo_handler;
	ngx_conf_set_str_slot(cf, cmd, conf);
	return NGX_CONF_OK;
}

static char* ngx_http_hello_counters(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
	printf("called:ngx_http_hello_counters\n");
	ngx_http_core_loc_conf_t *clcf;
	clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	clcf->handler = ngx_http_echo_handler;
	ngx_conf_set_num_slot(cf, cmd, conf);
	return NGX_CONF_OK;
}